﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LINQPad.Extensibility.DataContext;
using Xtensive.Orm.Model;

namespace LINQPadDriver
{
  public class ModelCacheContainer:MarshalByRefObject
  {
    private DomainModel _model;

    public ModelCacheContainer(DomainModel model)
    {
      _model = model;
    }

    public IEnumerable<string> GetAssemblyNames()
    {
      return _model.Types.Select(a => a.UnderlyingType.Assembly.GetName().Name).Distinct().ToArray();
    }

    public IEnumerable<string> GetNamespaces()
    {
      return
        _model.Hierarchies.SelectMany(a => a.Types)
          .Where(a => !a.IsSystem && !a.IsAuxiliary)
          .Select(a => a.UnderlyingType.Namespace)
          .Distinct().ToArray();
    }

    public List<ExplorerItem> GetExploreItems()
    {
      var items = _model.Hierarchies.SelectMany(a => a.Types).OrderBy(a => a.Name).Where(
        a => !a.IsAuxiliary && !a.IsSystem).Select
        (
          type =>
            new ExplorerItem(type.Name, ExplorerItemKind.QueryableObject, ExplorerIcon.Table) {
              IsEnumerable = true,
              ToolTipText = DataContextDriver.FormatTypeName(type.UnderlyingType, false),
              DragText = string.Format("Query.All<{0}>()", type.UnderlyingType.Name),
              Tag = type
            }).ToList();
      foreach (ExplorerItem item in items) {
        item.Children = new List<ExplorerItem>();
        foreach (FieldInfo field in
          ((TypeInfo) item.Tag).Fields.Where(a => !a.IsSystem)) {
          var eItem = new ExplorerItem(
            string.Format("{0} ({1})", field.Name, DataContextDriver.FormatTypeName(field.ValueType, false)),
            ExplorerItemKind.Property,
            ExplorerIcon.Column);
          if (field.IsPrimaryKey)
            eItem.Icon = ExplorerIcon.Key;
          AssociationInfo association = field.Associations.FirstOrDefault();
          if (association!=null) {
            switch (association.Multiplicity) {
              case Multiplicity.ManyToMany:
                eItem.Icon = ExplorerIcon.ManyToMany;
                eItem.Kind = ExplorerItemKind.CollectionLink;
                break;
              case Multiplicity.ManyToOne:
              case Multiplicity.ZeroToOne:
                eItem.Icon = ExplorerIcon.ManyToOne;
                eItem.Kind = ExplorerItemKind.ReferenceLink;
                break;
              case Multiplicity.OneToOne:
                eItem.Icon = ExplorerIcon.OneToOne;
                eItem.Kind = ExplorerItemKind.ReferenceLink;
                break;
              case Multiplicity.OneToMany:
              case Multiplicity.ZeroToMany:
                eItem.Kind = ExplorerItemKind.CollectionLink;
                eItem.Icon = ExplorerIcon.OneToMany;
                break;
            }
            eItem.HyperlinkTarget = items.FirstOrDefault(a => a.Tag.Equals(association.TargetType));
          }
          item.Children.Add(eItem);
        }
      }
      foreach (var item in items) {
        item.Tag = null;
      }
      return items;
    }

    public string GetWrappers()
    {
      var sb = new StringBuilder();
      sb.Append("namespace LINQPadDriver.Wrappers { ");
      {
        foreach (var type in _model.Types.Where(a => !a.IsSystem && !a.IsStructure)) {
          sb.Append("[System.Data.Linq.Mapping.Table] ");
          sb.AppendFormat("public class {0}Wrapper:GridWrapper {{ ", type.Name);
          {
            foreach (var field in type.Fields.Where(a => !a.IsSystem && !a.IsStructure)) {
              if (!field.IsEntity && !field.IsEntitySet)
                sb.AppendFormat(
                  "[System.Data.Linq.Mapping.Column(IsPrimaryKey={0})] ",
                  field.IsPrimaryKey ? "true" : "false");
              sb.AppendFormat(
                "public {0} {1} {{get{{return ({0})GetField(\"{2}\");}} set{{SetField(\"{2}\", value);}} }} ",
                TypeToName(field.ValueType),
                field.Name.Replace('.', '_'),
                field.Name);
            }
          }
          sb.Append("}");
        }
      }
      sb.Append(" } ");
      return sb.ToString();
    }

    private string TypeToName(Type type)
    {
      if (!type.IsGenericType)
        return type.FullName;
      var types = type.GetGenericArguments();
      type = type.GetGenericTypeDefinition();
      var s = type.FullName.Substring(0, type.FullName.IndexOf('`')) + "<" +
              string.Join(",", types.Select(a => a.FullName)) + ">";
      return s;
    }
  }
}
